import Chess from "./Chess"

const { ccclass, property } = cc._decorator


const GRID_SIZE = 15
const GRID_WIDTH = 50

const CHESS_BLACK = 'black'
const CHESS_WHITE = 'white'

const GRID_INIT = [7, 7]


@ccclass
export default class Game extends cc.Component {
    @property(cc.Prefab)
    private chessPrefab: cc.Prefab = null

    @property(cc.Node)
    private bgNode: cc.Node = null
    @property(cc.Node)
    private rootNode: cc.Node = null

    // 已下棋的格子
    private chessMap: Map<number, Chess> = new Map()

    // 该谁下棋了(black | white)
    private _turn: string
    // 最后下的棋子(有 红点 标识)
    private _lastChess: Chess


    protected onLoad(): void {
        this.init()
    }

    protected start(): void {
        this._turn = CHESS_BLACK
        this.drawChess(GRID_INIT[0], GRID_INIT[1])
        this.turn()
    }


    private init() {
        const node = new cc.Node()
        node.parent = this.bgNode
        node.setAnchorPoint(cc.Vec2.ZERO)

        const graphics = node.addComponent(cc.Graphics)
        graphics.lineWidth = 3 //我电脑显示器不行，要设置宽度为 3 才能显示线条，如果没问题的可以注释掉这一行
        graphics.strokeColor = cc.Color.RED

        // 从下往上 画 横线
        for (let col = 0; col < GRID_SIZE; col++) {
            graphics.moveTo(GRID_WIDTH / 2, col * GRID_WIDTH + GRID_WIDTH / 2)
            graphics.lineTo(GRID_SIZE * GRID_WIDTH - GRID_WIDTH / 2, col * GRID_WIDTH + GRID_WIDTH / 2)
            graphics.stroke()
        }
        // 从左往右 画 竖线
        for (let row = 0; row < GRID_SIZE; row++) {
            graphics.moveTo(row * GRID_WIDTH + GRID_WIDTH / 2, GRID_WIDTH / 2)
            graphics.lineTo(row * GRID_WIDTH + GRID_WIDTH / 2, GRID_SIZE * GRID_WIDTH - GRID_WIDTH / 2)
            graphics.stroke()
        }

        this.rootNode.on(cc.Node.EventType.TOUCH_START, (event: cc.Event.EventTouch) => {
            const pos = this.rootNode.convertToNodeSpaceAR(event.getLocation())
            const [col, row] = this.posToGrid(pos)

            const idx = this.gridToIdx(col, row)
            if (this.chessMap.has(idx)) return

            this.drawChess(col, row)
            this.checkWin(col, row)
            this.turn()
        })

        this.panelSettleInit()
    }


    private _panelSettle: cc.Node

    private panelSettleInit() {
        this._panelSettle = this.node.getChildByName('panelSettle')
        this._panelSettle.active = false
    }

    private panelSettleShow(msg: string) {
        this._panelSettle.getChildByName('lblMsg').getComponent(cc.Label).string = msg
        this._panelSettle.getChildByName('btnOk').on('click', () => cc.game.restart())

        this._panelSettle.active = true
    }


    private turn() {
        this._turn = this._turn == CHESS_BLACK ? CHESS_WHITE : CHESS_BLACK
    }

    private drawChess(col: number, row: number) {
        const idx = this.gridToIdx(col, row)

        const chess = cc.instantiate(this.chessPrefab)
        chess.parent = this.rootNode
        chess.setPosition(this.gridToPos(col, row))

        if (this._lastChess) {
            this._lastChess.redHide()
        }

        const chessComp = chess.getComponent(Chess)
        chessComp.init(this._turn)
        chessComp.redShow()

        this.chessMap.set(idx, chessComp)
        this._lastChess = chessComp
    }

    private checkWin(col: number, row: number) {
        let datas: Chess[] = []

        // 左至右
        for (let i = row - 4; i <= row + 4; i++) {
            datas.push(this.chessMap.get(this.gridToIdx(col, i)))
        }
        if (this.checkWin2(datas)) return

        // 下至上
        datas = []
        for (let i = col - 4; i <= col + 4; i++) {
            datas.push(this.chessMap.get(this.gridToIdx(i, row)))
        }
        if (this.checkWin2(datas)) return

        // 左下到右上
        datas = []
        for (let i = - 4; i <= 4; i++) {
            datas.push(this.chessMap.get(this.gridToIdx(col + i, row + i)))
        }
        if (this.checkWin2(datas)) return

        // 左上到右下
        datas = []
        for (let i = - 4; i <= 4; i++) {
            datas.push(this.chessMap.get(this.gridToIdx(col - i, row + i)))
        }
        if (this.checkWin2(datas)) return

        if (this.chessMap.size == GRID_SIZE * GRID_SIZE) {
            this.panelSettleShow('平局')
        }
    }

    private checkWin2(datas: Chess[]): boolean {
        for (let i = 0; i <= datas.length - 4; i++) {
            // 判断是否等于当前下的棋子
            if (datas[i]?.chessName != this._turn) continue

            // 到这里就是有一颗棋子了
            let sameNum = 1
            for (let j = i + 1; j < datas.length; j++) {
                if (datas[j]?.chessName != this._turn) break
                sameNum++
                if (sameNum >= 5) {
                    const msg = this._turn == CHESS_BLACK ? '黑棋赢了' : '白棋赢了'
                    this.panelSettleShow(msg)
                    return true
                }
            }
        }

        return false
    }


    // 检查输赢的时候格子有越界情况，在这里判断，返回一个不在正常范围内的索引 [0, 224(15 * 15 -1)]
    private gridToIdx(col: number, row: number): number {
        if (col < 0 || col >= GRID_SIZE || row < 0 || row >= GRID_SIZE) return -1
        return col * GRID_SIZE + row
    }

    private gridToPos(col: number, row: number): cc.Vec2 {
        return cc.v2(row * GRID_WIDTH + GRID_WIDTH / 2, col * GRID_WIDTH + GRID_WIDTH / 2)
    }

    private posToGrid(pos: cc.Vec2): number[] {
        return [Math.floor(pos.y / GRID_WIDTH), Math.floor(pos.x / GRID_WIDTH)]
    }


    // 棋盘随机显示所有棋子
    private showAll() {
        for (let col = 0; col < GRID_SIZE; col++) {
            for (let row = 0; row < GRID_SIZE; row++) {
                const chess = cc.instantiate(this.chessPrefab)
                chess.parent = this.rootNode
                chess.setPosition(this.gridToPos(col, row))

                const name = this.randRange(0, 1) == 1 ? CHESS_BLACK : CHESS_WHITE

                const chessComp = chess.getComponent(Chess)
                chessComp.init(name)
            }
        }
    }

    // 整数范围随机 [min, max]
    private randRange(min: number, max: number): number {
        return Math.round(Math.random() * (max - min) + min)
    }
}
